﻿using Common.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Wicresoft.EKA.CommonExtensions;

namespace Wicresoft.EKA.NRemedy
{

    public class ARLoginContextPool : ICleanUp
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(ARLoginContextPool));

        private static ARLoginContextPool _instance;

        private static readonly Object lockObject = new Object();

        private Dictionary<string, ARLoginContextAndLastUsedTime> items;

        private ARLoginContextPool()
        {
            items = new Dictionary<string, ARLoginContextAndLastUsedTime>();
            CleanUpThread cleanThread = new CleanUpThread(this, this._sleepTime);
            Thread t = new Thread(new ThreadStart(cleanThread.run));
            t.Start();
        }

        public static ARLoginContextPool Instance
        {
            get
            {
                if (_instance != null) return _instance;
                lock (lockObject)
                {
                    if (_instance == null)
                    {
                        _instance = new ARLoginContextPool();
                    }
                    return _instance;
                }
            }
        }

        internal ARLoginContextSerializers GetLoginContextSerializer(bool remove)
        {
            ARLoginContextSerializers contexts = new ARLoginContextSerializers();
            IList<ARLoginContextSerializer> context = new List<ARLoginContextSerializer>();
            IList<string> keys = new List<string>();
            SymmetricMethod me = new SymmetricMethod();
            lock (items)
            {
                foreach (var item in items)
                {
                    logger.Info("uninit from application start    " + item.Value.LogInContext.User);
                    context.Add(new ARLoginContextSerializer()
                        {
                            Authentication = me.Encrypto(item.Value.LogInContext.Authentication),
                            Password = me.Encrypto(item.Value.LogInContext.Password),
                            Server = me.Encrypto(item.Value.LogInContext.Server),
                            User = me.Encrypto(item.Value.LogInContext.User),
                        });
                    keys.Add(item.Key);
                }
            }

            if (remove)
            {
                foreach (var item in keys)
                {
                    items.Remove(item);
                }

            }

            contexts.Contexts = context.ToArray();
            return contexts;
        }

        internal void InitContextPoolFromSerializer(ARLoginContextSerializers contexts)
        {
            lock (items)
            {
                SymmetricMethod me = new SymmetricMethod();
                foreach (var item in contexts.Contexts)
                {
                    logger.Info("init from application start    " + me.Decrypto(item.User));
                    ARLoginContextPool.Instance.Add(me.Decrypto(item.User), new ARLoginContext(me.Decrypto(item.Server),
                   me.Decrypto(item.User), me.Decrypto(item.Password), me.Decrypto(item.Authentication)));
                }
            }
        }

        public void Remove(string name)
        {
            lock (items)
            {
                logger.Info("Name : Remove " + name);
                if (items.ContainsKey(name))
                {
                    items[name].Dispose();
                    items.Remove(name);
                }
            }
        }

        public void Add(string name, ARLoginContext context)
        {
            lock (items)
            {
                if (items.ContainsKey(name))
                {
                    //items[name].Dispose();
                    //items.Remove(name);
                    return;
                }
                int count = items.Keys.Count;
                if (count < this._poolSize || 0 == _poolSize)
                {
                    items.Add(name, new ARLoginContextAndLastUsedTime() { LogInContext = context, LastUsedTime = DateTime.Now });
                }
                else
                {
                    DateTime min;
                    string keyMin = string.Empty;

                    while (count >= this._poolSize)
                    {
                        min = items.Select(o => o.Value.LastUsedTime).Min();
                        keyMin = items.First(p => p.Value.LastUsedTime == min).Key;

                        items[keyMin].Dispose();

                        items.Remove(keyMin);
                        count = items.Keys.Count;
                    }

                }
            }

        }


        public IARServer CreateARServer()
        {
            return new ARServer();
        }

        public ARLoginContext GetLoginContext(string key)
        {
            //lock (items)
            //{
            if (items.ContainsKey(key))
            {
                ARLoginContextAndLastUsedTime item = items[key];
                item.LastUsedTime = DateTime.Now;
                return item.LogInContext;
            }
            else
            {
                logger.Error("The ARLoginContext pool don't have the Key: " + key + items.Count + "    " + items.Keys);
                throw new InvalidOperationException("The ARLoginContext pool don't have the Key: " + key);
            }

            //}
        }

        public bool ContainsKey(string key)
        {
            return items.ContainsKey(key);
        }

        private int _expirationTime = 2 * 60 * 60 * 1000;

        public int ExpirationTime
        {
            get
            {
                return this._expirationTime;
            }
            set
            {
                this._expirationTime = value;
            }
        }

        private int _sleepTime = 5 * 60 * 1000;

        public int SleepTime
        {
            get
            {
                return this._sleepTime;
            }
            set
            {
                this._sleepTime = value;
            }
        }

        private int _poolSize = 0;

        public int PoolSize
        {
            get
            {
                return this._poolSize;
            }
            set
            {
                this._poolSize = value;
            }
        }

        public void Clean()
        {
            lock (items)
            {
                logger.Info("Clean  Up  " + items.Count);
                var expires = items.Where(i => (i.Value.LastUsedTime.AddMilliseconds(_expirationTime)) <= (DateTime.Now)).ToList();
                foreach (var item in expires)
                {
                    ARLoginContextAndLastUsedTime asa = item.Value;
                    if (asa.LastUsedTime.AddMilliseconds(_expirationTime) <= DateTime.Now)
                    {
                        logger.Info("Clean  Up  " + items.Count + "     " + item.Key + "    " + item.Value.LastUsedTime);
                        items.Remove(item.Key);
                        asa.Dispose();
                    }
                }
            }
        }


    }
}
